terraform {
  required_providers {
    coder = {
      source = "coder/coder"
    }
    docker = {
      source = "kreuzwerker/docker"
    }
  }
}

locals {
  username = data.coder_workspace.me.owner
}

data "coder_provisioner" "me" {
}

data "coder_parameter" "anaconda_env_name" {
  name        = "Anaconda Environment Name"
  description = "Anaconda Environment Name"
  type        = "string"
  default     = "workspace"
}

data "coder_parameter" "python_version" {
  name        = "Python Version"
  description = "Anaconda Python Version"
  type        = "string"
  default     = "3.12"
}

data "coder_parameter" "node_version" {
  name        = "NodeJS Version"
  description = "NodeJS Version"
  type        = "string"
  default     = "20.11.1"
}

provider "docker" {
}

data "coder_workspace" "me" {
}

resource "coder_agent" "main" {
  arch                   = data.coder_provisioner.me.arch
  os                     = "linux"
  startup_script_timeout = 180
  startup_script         = <<-EOT
    set -e
    
    sudo chown -R ${local.username} $JAVA_HOME
    sudo chown -R ${local.username} $GRADLE_HOME
    sudo chown -R ${local.username} $ANDROID_HOME

    # install zsh, oh-my-zsh and conda init
    if [ ! -d "/home/${local.username}/.oh-my-zsh" ]; then
      sh -c "$(wget -O- https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
    fi

    if [ ! -f "/home/${local.username}/.condarc" ]; then
      cp /opt/.condarc /home/${local.username}
      $CONDA_HOME/bin/conda init zsh
    fi

    if [ ! -d "$CONDA_HOME/envs/${data.coder_parameter.anaconda_env_name.value}" ]; then
      sudo chown -R ${local.username} $CONDA_HOME/envs
      $CONDA_HOME/bin/conda create -n ${data.coder_parameter.anaconda_env_name.value} -y python=${data.coder_parameter.python_version.value} anaconda
      echo "source activate ${data.coder_parameter.anaconda_env_name.value}" >> /home/${local.username}/.zshrc
    fi

    if [ ! -d "$NVM_DIR/versions/node/v${data.coder_parameter.node_version.value}" ]; then
      sudo chown -R ${local.username} $NVM_DIR
      . $NVM_DIR/nvm.sh && nvm install ${data.coder_parameter.node_version.value} && nvm alias default ${data.coder_parameter.node_version.value} && nvm use default
    fi

    # install and start code-server
    # curl -fsSL https://code-server.dev/install.sh | sh -s -- --method=standalone --prefix=/tmp/code-server --version 4.19.1
    /opt/code-server/bin/code-server --auth none --port 13337 >/tmp/code-server.log 2>&1 &

    # install and start jupyterlab
    /opt/miniconda/envs/workspace/bin/jupyter lab --ServerApp.token='' --ip='*' --ServerApp.base_url=/@${data.coder_workspace.me.owner}/${lower(data.coder_workspace.me.name)}/apps/jupyter >/tmp/jupyter.log 2>&1 &
  EOT

  # These environment variables allow you to make Git commits right away after creating a
  # workspace. Note that they take precedence over configuration defined in ~/.gitconfig!
  # You can remove this block if you'd prefer to configure Git manually or using
  # dotfiles. (see docs/dotfiles.md)
  env = {
    GIT_AUTHOR_NAME     = coalesce(data.coder_workspace.me.owner_name, data.coder_workspace.me.owner)
    GIT_AUTHOR_EMAIL    = "${data.coder_workspace.me.owner_email}"
    GIT_COMMITTER_NAME  = coalesce(data.coder_workspace.me.owner_name, data.coder_workspace.me.owner)
    GIT_COMMITTER_EMAIL = "${data.coder_workspace.me.owner_email}"
    PATH                = "$NVM_DIR/versions/node/v${data.coder_parameter.node_version.value}/bin:$PATH"
  }

  # The following metadata blocks are optional. They are used to display
  # information about your workspace in the dashboard. You can remove them
  # if you don't want to display any information.
  # For basic resources, you can use the `coder stat` command.
  # If you need more control, you can write your own script.
  metadata {
    display_name = "CPU Usage"
    key          = "0_cpu_usage"
    script       = "coder stat cpu"
    interval     = 10
    timeout      = 1
  }

  metadata {
    display_name = "RAM Usage"
    key          = "1_ram_usage"
    script       = "coder stat mem"
    interval     = 10
    timeout      = 1
  }

  metadata {
    display_name = "Home Disk"
    key          = "3_home_disk"
    script       = "coder stat disk --path $${HOME}"
    interval     = 60
    timeout      = 1
  }

  metadata {
    display_name = "CPU Usage (Host)"
    key          = "4_cpu_usage_host"
    script       = "coder stat cpu --host"
    interval     = 10
    timeout      = 1
  }

  metadata {
    display_name = "Memory Usage (Host)"
    key          = "5_mem_usage_host"
    script       = "coder stat mem --host"
    interval     = 10
    timeout      = 1
  }

  metadata {
    display_name = "Load Average (Host)"
    key          = "6_load_host"
    # get load avg scaled by number of cores
    script   = <<EOT
      echo "`cat /proc/loadavg | awk '{ print $1 }'` `nproc`" | awk '{ printf "%0.2f", $1/$2 }'
    EOT
    interval = 60
    timeout  = 1
  }

  metadata {
    display_name = "Swap Usage (Host)"
    key          = "7_swap_host"
    script       = <<EOT
      free -b | awk '/^Swap/ { printf("%.1f/%.1f", $3/1024.0/1024.0/1024.0, $2/1024.0/1024.0/1024.0) }'
    EOT
    interval     = 10
    timeout      = 1
  }
}

resource "coder_app" "code-server" {
  agent_id     = coder_agent.main.id
  slug         = "code-server"
  display_name = "code-server"
  url          = "http://localhost:13337/?folder=/home/${local.username}/workspace"
  icon         = "/icon/code.svg"
  subdomain    = false
  share        = "owner"

  healthcheck {
    url       = "http://localhost:13337/healthz"
    interval  = 5
    threshold = 6
  }
}

resource "coder_app" "jupyter" {
  agent_id     = coder_agent.main.id
  slug         = "jupyter"
  display_name = "JupyterLab"
  url          = "http://localhost:8888/@${data.coder_workspace.me.owner}/${lower(data.coder_workspace.me.name)}/apps/jupyter"
  icon         = "/icon/jupyter.svg"
  share        = "owner"
  subdomain    = false

  healthcheck {
    url       = "http://localhost:8888/@${data.coder_workspace.me.owner}/${lower(data.coder_workspace.me.name)}/apps/jupyter"
    interval  = 5
    threshold = 10
  }
}

resource "docker_volume" "home_volume" {
  name = "coder-${data.coder_workspace.me.id}-home"
  # Protect the volume from being deleted due to changes in attributes.
  lifecycle {
    ignore_changes = all
  }
  # Add labels in Docker to keep track of orphan resources.
  labels {
    label = "coder.owner"
    value = data.coder_workspace.me.owner
  }
  labels {
    label = "coder.owner_id"
    value = data.coder_workspace.me.owner_id
  }
  labels {
    label = "coder.workspace_id"
    value = data.coder_workspace.me.id
  }
  # This field becomes outdated if the workspace is renamed but can
  # be useful for debugging or cleaning out dangling volumes.
  labels {
    label = "coder.workspace_name_at_creation"
    value = data.coder_workspace.me.name
  }
}

resource "docker_volume" "conda_envs_volume" {
  name = "coder-${data.coder_workspace.me.id}-conda-envs"
  lifecycle {
    ignore_changes = all
  }
}

resource "docker_volume" "node_versions_volume" {
  name = "coder-${data.coder_workspace.me.id}-node-versions"
  lifecycle {
    ignore_changes = all
  }
}

resource "docker_volume" "gradle_cache_volume" {
  name = "coder-${data.coder_workspace.me.id}-gradle-cache"
  lifecycle {
    ignore_changes = all
  }
}

resource "docker_image" "main" {
  name = "coder-${data.coder_workspace.me.id}"
  build {
    context = "./build"
    build_args = {
      USER = local.username
    }
  }
  triggers = {
    dir_sha1 = sha1(join("", [for f in fileset(path.module, "build/*") : filesha1(f)]))
  }
}

resource "docker_container" "workspace" {
  count = data.coder_workspace.me.start_count
  image = docker_image.main.name
  # Uses lower() to avoid Docker restriction on container names.
  name = "coder-${data.coder_workspace.me.owner}-${lower(data.coder_workspace.me.name)}"
  # Hostname makes the shell more user friendly: coder@my-workspace:~$
  hostname = data.coder_workspace.me.name
  # Use the docker gateway if the access URL is 127.0.0.1
  entrypoint = ["sh", "-c", replace(coder_agent.main.init_script, "/localhost|127\\.0\\.0\\.1/", "host.docker.internal")]
  env        = ["CODER_AGENT_TOKEN=${coder_agent.main.token}"]
  gpus       = "all"
  host {
    host = "host.docker.internal"
    ip   = "host-gateway"
  }
  volumes {
    container_path = "/home/${local.username}"
    volume_name    = docker_volume.home_volume.name
    read_only      = false
  }
  volumes {
    container_path = "/opt/miniconda/envs"
    volume_name    = docker_volume.conda_envs_volume.name
    read_only      = false
  }
  volumes {
    container_path = "/opt/nvm/versions"
    volume_name    = docker_volume.node_versions_volume.name
    read_only      = false
  }
  volumes {
    container_path = "/opt/gradle/gradle-8.6/caches"
    volume_name    = docker_volume.gradle_cache_volume.name
    read_only      = false
  }
  volumes {
    container_path = "/home/${local.username}/workspace"
    host_path = "/home/fansbasa/Documents/workspace"
    read_only = false
  }
  volumes {
    container_path = "/var/run/docker.sock"
    host_path = "/var/run/docker.sock"
    read_only = true
  }

  # Add labels in Docker to keep track of orphan resources.
  labels {
    label = "coder.owner"
    value = data.coder_workspace.me.owner
  }
  labels {
    label = "coder.owner_id"
    value = data.coder_workspace.me.owner_id
  }
  labels {
    label = "coder.workspace_id"
    value = data.coder_workspace.me.id
  }
  labels {
    label = "coder.workspace_name"
    value = data.coder_workspace.me.name
  }
}
